package persistence;

import java.sql.*;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.Conversation;
import model.Message;
import model.User;

public class ConversationDAOJDBC implements ConversationDAO {

    private final Object lock = new Object();
    private static ConversationDAOJDBC instance = null;
    private Connection connection = null;
    private static final Logger log = Logger.getLogger(ConversationDAOJDBC.class.getName());

    public ConversationDAOJDBC() {
    }
//@Abraham potencial problema de sincronizacion en la creación del objeto; pueden crearse varios
    public static ConversationDAO getInstance() {
        if (instance == null) {
            instance = new ConversationDAOJDBC();
        }
        return instance;
    }

    @Override
    public boolean connection(String user, String pass, String driver, String url) {
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user, pass);
        } catch (ClassNotFoundException | SQLException ex) {
            log.log(Level.SEVERE, "Ocurrió un error al intentar conectar con la base de datos", ex);
            return false;
        }
        return true;
    }

    @Override
    public boolean disconnect() {
        try {
            connection.close();
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "No se pudo cerrar la conexión con la base de datos", ex);
            return false;
        }
        return true;
    }

    @Override
    public boolean newConversation(Conversation conversation) {
        String query = "INSERT INTO \"CONVERSATIONS\" VALUES(?,?,?)";
        PreparedStatement statement;
        try {
            synchronized (lock) {
                statement = connection.prepareStatement(query);
            }
            statement.setString(1, conversation.getConversationID());
            statement.setString(2, conversation.getConversationDateStart());
            statement.setString(3, conversation.getConversationDateEnd());
            statement.execute();
            ArrayList<User> users = conversation.getConversationAllUsers();
            boolean usersCorrect = setUsersOfTheConversation(users, statement,
                    conversation.getConversationID());
            ArrayList<Message> messages = conversation.getConversationMessages();
            boolean messagesCorrect = setMessagesOfTheConversation(messages, statement,
                    conversation.getConversationID());
            if (usersCorrect && messagesCorrect) {
                return true;
            } else {
                return false;
            }
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al crear la conversación", ex);
            return false;
        }
    }

    @Override
    public ArrayList<Conversation> getConversationMap() {
        String query = "SELECT * FROM \"CONVERSATIONS\" c";
        PreparedStatement statement;
        ResultSet resultSet = null;
        Conversation c;
        ArrayList<Conversation> listOfConversations = new ArrayList<>();
        try {
            synchronized (lock) {
                statement = connection.prepareStatement(query);
            }
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                c = new Conversation();
                c.setConversationID(resultSet.getString("CONVERSATIONID"));
                c.setConversationDateStart(resultSet.getString("CONVERSATIONDATESTART"));
                c.setConversationDateEnd(resultSet.getString("CONVERSATIONDATEEND"));
                c.setConversationAllUsers(getUsersOfTheConversation(c.getConversationID()));
                c.setConversationMessages(getMessagesOfTheConversation(c.getConversationID()));
                listOfConversations.add(c);
            }
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al crear la conversación", ex);
            return null;
        } finally {
            //@Abraham falta por cerrar el statement
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException ex) {
                    log.log(Level.SEVERE, "Fallo al cerrar el ResultSet", ex);
                }
            }
        }
        return listOfConversations;
    }

    @Override
    public ArrayList<Conversation> getConversationMap(User user) {
        String query = "SELECT * FROM \"CONVERSATIONS\" c, \"USERS_CONVERSATIONS\" uc "
                + " WHERE c.CONVERSATIONID = uc.CONVERSATIONID "
                + " AND uc.USEREMAIL = ?";
        PreparedStatement statement;
        ResultSet resultSet = null;
        Conversation c;
        ArrayList<Conversation> listOfConversations = new ArrayList<>();
        try {
            synchronized (lock) {
                statement = connection.prepareStatement(query);
            }
            statement.setString(1, user.getUserEmail());
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                c = new Conversation();
                c.setConversationID(resultSet.getString("CONVERSATIONID"));
                c.setConversationDateStart(resultSet.getString("CONVERSATIONDATESTART"));
                c.setConversationDateEnd(resultSet.getString("CONVERSATIONDATEEND"));
                c.setConversationAllUsers(getUsersOfTheConversation(c.getConversationID()));
                c.setConversationMessages(getMessagesOfTheConversation(c.getConversationID()));
                listOfConversations.add(c);
            }
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al crear la conversación", ex);
            return null;
        } finally {
            //@Abraham falta por cerrar el statement
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException ex) {
                    log.log(Level.SEVERE, "Fallo al cerrar el ResultSet", ex);
                }
            }
        }
        return listOfConversations;
    }

    public boolean setMessagesOfTheConversation(ArrayList<Message> messages,
            PreparedStatement statement, String conversationID) {
        String query;
        try {
            for (int i = 0; i < messages.size(); i++) {
                query = "INSERT INTO \"MESSAGES\" (\"CONVERSATIONID\", \"MESSAGECONTENT\") VALUES (?,?)";
                synchronized (lock) {
                    statement = connection.prepareStatement(query);
                }
                statement.setString(1, conversationID);
                statement.setString(2, messages.get(i).toString());
                statement.execute();
            }
            return true;
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al guardar los mensajes de la conversación", ex);
            return false;
        }
        //@Abraham falta por cerrar el statement
    }

    public ArrayList<Message> getMessagesOfTheConversation(String conversationID) {
        ArrayList<Message> messages = new ArrayList<>();
        String query = "SELECT * FROM \"CONVERSATIONS\" c, \"MESSAGES\" m "
                + " WHERE m.CONVERSATIONID = c.CONVERSATIONID "
                + " AND c.CONVERSATIONID = ?";
        PreparedStatement statement;
        ResultSet resultSet = null;
        Message m;
        try {
            synchronized (lock) {
                statement = connection.prepareStatement(query);
            }
            statement.setString(1, conversationID);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                String content = resultSet.getString("MESSAGECONTENT");
                m = Message.stringToMessage(content);
                messages.add(m);
            }
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al crear la conversación", ex);
            return null;
        } finally {
            //@Abraham falta por cerrar el statement
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException ex) {
                    log.log(Level.SEVERE, "Fallo al cerrar el ResultSet", ex);
                }
            }
        }
        return messages;
    }

    public boolean setUsersOfTheConversation(ArrayList<User> users,
            PreparedStatement statement, String conversationID) {
        String query;
        try {
            for (int i = 0; i < users.size(); i++) {
                query = "INSERT INTO \"USERS_CONVERSATIONS\" VALUES (?,?)";
                synchronized (lock) {
                    statement = connection.prepareStatement(query);
                }
                statement.setString(1, conversationID);
                statement.setString(2, users.get(i).getUserEmail());
                statement.execute();
            }
            return true;
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al guardar los usuarios de la conversación", ex);
            return false;
        }
        //@Abraham falta por cerrar el statement
    }

    public ArrayList<User> getUsersOfTheConversation(String conversationID) {
        ArrayList<User> users = new ArrayList<>();
        String query = "SELECT * FROM \"CONVERSATIONS\" c, \"USERS_CONVERSATIONS\" um, \"USERS\" u "
                + " WHERE c.CONVERSATIONID = um.CONVERSATIONID "
                + " AND um.USEREMAIL = u.USEREMAIL "
                + " AND c.CONVERSATIONID = ?";
        PreparedStatement statement;
        ResultSet resultSet = null;
        try {
            synchronized (lock) {
                statement = connection.prepareStatement(query);
            }
            statement.setString(1, conversationID);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                User u = new User();
                u.setUserName(resultSet.getString("USERNAME"));
                u.setUserNickName(resultSet.getString("USERNICKNAME"));
                u.setUserEmail(resultSet.getString("USEREMAIL"));
                u.setUserPassword(resultSet.getString("USERPASSWORD"));
                String privileged = resultSet.getString("USERPRIVILEGED");
                if (privileged.equals("t")) {
                    u.setUserPrivileged(true);
                } else {
                    u.setUserPrivileged(false);
                }
                users.add(u);
            }
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Error al crear la conversación", ex);
            return null;
        } finally {
            //@Abraham falta por cerrar el statement
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException ex) {
                    log.log(Level.SEVERE, "Fallo al cerrar el ResultSet", ex);
                }
            }
        }
        return users;
    }
}
